# -*- coding: UTF-8 -*-
#!/usr/local/bin/python

#Copyright (C) 2006 by raptor
#
#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; either version 2
#of the License, or (at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# convert to epub
# author  : raptor@we8log.com
# created : 2011/08/14

# modified to support python3.3 and large files
# author: phshentu
# time: 2014/04/22

import os
import re
import tempfile
import shutil
import zipfile
from datetime import date
from uuid import uuid5, NAMESPACE_URL
import traceback


imgname_pattern = re.compile("[0-9].*")


css_content = '''
body {
    font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
    margin : 0px;
}
h3 {
    border-bottom : 1px solid #AAA;
}
'''
# fields: title, css, content
html_template = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN">
<head><title>%(title)s</title>
<link href="%(css)s" rel="stylesheet" type="text/css"/></head>
<body>%(content)s</body></html>'''

# fields: title, uuid, author, published, year, author, manifest, spine, guide
opf_template = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="PrimaryID" version="2.0">
    <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
        <dc:title>%(title)s</dc:title>
        <dc:language>zh-CN</dc:language>
        <dc:identifier id="PrimaryID">urn:uuid:%(uuid)s</dc:identifier>
        <dc:creator opf:role="aut">%(author)s</dc:creator>
        <dc:publisher>Blog2epub.py</dc:publisher>
        <dc:date opf:event="publication">%(published)s</dc:date>
        <dc:rights>Copyright © %(year)s by %(author)s</dc:rights>
        <meta content="cover-image" name="cover"/>
    </metadata>
  <manifest>
%(manifest)s
  </manifest>
  <spine toc="ncx">
%(spine)s
  </spine>
  <guide>
%(guide)s
  </guide>
</package>'''

# fields: filename, id, mtype(application/x-dtbncx+xml, text/css, application/xhtml+xml, image/jpeg)
manifest_template = '''    <item href="%s" id="%s" media-type="%s"/>
'''

# fields: id, linear(linear="no")
spine_template = '''    <itemref idref="%s" %s/>
'''

# fields: filename, title, gtype(start, toc, copyright-page, thumbimagestandard, title-page, text)
guide_template = '''    <reference href="%s" title="%s" type="%s"/>
'''

# fields: uid, title, author, navmap
ncx_template = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
<ncx version="2005-1" xml:lang="en-US" xmlns="http://www.daisy.org/z3986/2005/ncx/">
<head>
<meta name="dtb:PrimaryID" content="%(uid)s"/>
<meta name="dtb:depth" content="2"/>
    <meta name="dtb:totalPageCount" content="0"/>
    <meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle><text>%(title)s</text></docTitle>
<docAuthor><text>%(author)s</text></docAuthor>
<navMap>
%(navmap)s
</navMap>
</ncx>'''

# fields: id, order, title, filename
navpoint_template = '''
<navPoint id="%s" playOrder="%d">
<navLabel><text>%s</text></navLabel>
<content src="%s"/>
</navPoint>
'''


class EpubData:

    def __init__(self):
        self.dest_dir  = tempfile.mkdtemp()
        self._init_dir()
        self.meta_dir  = self._init_dir('META-INF')
        self.oebps_dir = self._init_dir('OEBPS')
        self.img_idr   = self._init_dir('images', self.oebps_dir)
        self.opf_name  = 'epub_opf.opf'
        self.ncx_name  = 'epub_ncx.ncx'
        self.css_name  = 'epub_css.css'
        self._init_file(self.dest_dir, 'mimetype', 'application/epub+zip')
        self._init_file(self.meta_dir, 'container.xml', """<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
  <rootfiles>
    <rootfile full-path="%s" media-type="application/oebps-package+xml"/>
  </rootfiles>
</container>""" % self.opf_name)
        self._init_file(self.oebps_dir, self.css_name, css_content)
        self.chp_list = []
        self.img_list = []

    def _init_dir(self, dirname=None, parent=None):
        if parent == None:
            parent = self.dest_dir
        if dirname != None:
            result = os.path.join(parent, dirname)
        else:
            result = self.dest_dir
        if not os.path.exists(result):
            os.mkdir(result)
        return result

    def _init_file(self, dirname, filename, content):
        f = open(os.path.join(dirname, filename), "w")
        f.write(content.encode('utf-8'))
        f.close()

    def _opf_content(self):
        # maifest
        manifest = manifest_template % (self.ncx_name, 'ncx', 'application/x-dtbncx+xml')
        manifest += manifest_template % ('OEBPS/%s' % self.css_name, 'style', 'text/css')
        manifest += manifest_template % ('OEBPS/coverpage.htm', 'coverpage', 'application/xhtml+xml')
        for chp in self.chp_list:
            fid = chp['id']
            manifest += manifest_template % ('OEBPS/%d.htm' % fid, 'c%d' % fid, 'application/xhtml+xml')
        manifest += manifest_template % ('OEBPS/images/cover.jpg', 'cover-image', 'image/jpeg')
        for img in self.img_list:
            fid = img['name']
            fid = (imgname_pattern.match(fid) == None) and fid or ("x_%s" % fid)
            try:
                fid, fext = os.path.splitext(fid)
            except:
                print ("Invalid image name" % img['name'])
                pass
            manifest += manifest_template % ('OEBPS/images/%(name)s' % img, fid, 'image/%(itype)s' % img)
        # spine
        spine = spine_template % ("coverpage", "")
        for chp in self.chp_list:
            spine += spine_template % ("c%(id)d" % chp, "")
        # guide
        guide = guide_template % ('OEBPS/coverpage.htm', '封面', 'cover')
        guide += guide_template % ('OEBPS/%(id)d.htm' % self.chp_list[0], 'start', 'start')
        # other
        dt = date.today()
        return opf_template % {'title': self.title, 'uuid': self.uuid, 'author': self.author, 'published': ("%04d-%02d-%02d" % (dt.year, dt.month, dt.day)), 'year': dt.year, 'manifest': manifest, 'spine': spine, 'guide': guide}

    def _ncx_content(self):
        navmap = navpoint_template % ('index', 1, self.title, 'OEBPS/index.htm')
        for i in range(len(self.chp_list)):
            chp = self.chp_list[i]
            navmap += navpoint_template % ('c%d' % chp['id'], i + 2, chp['title'], 'OEBPS/%d.htm' % chp['id'])
        return ncx_template % {'title': self.title, 'uid': self.uuid, 'author': self.author, 'navmap': navmap}

    def _zip_epub(self, filename):
        f = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
        cwd = os.getcwd()
        os.chdir(self.dest_dir)
        f.write('mimetype', compress_type=zipfile.ZIP_STORED)
        for root, dirs, files in os.walk("."):
            for fn in files:
                if fn != 'mimetype':
                    f.write(os.path.join(root, fn))
        f.close()
        os.chdir(cwd)

    def set_info(self, title, author, url="", uuid="", cover_name="images/cover.jpg"):
        self.title  = title
        self.author = author
        self.url    = url
        self.uuid   = (uuid == "") and str(uuid5(NAMESPACE_URL, url)) or uuid
        self.cover  = cover_name

    def add_chapter(self, fid, title, content):
        self._init_file(self.oebps_dir, "%d.htm" % fid, html_template % {"title": title, "css": self.css_name, "content": content})
        self.chp_list.append(dict(id=fid, title=title))

    def add_image(self, filename):
        basename = os.path.basename(filename)
        shutil.copyfile(filename, os.path.join(self.oebps_dir, 'images', basename))
        if basename != 'cover.jpg':
            try:
                fname, itype = os.path.splitext(basename)
                if (itype != 'gif' and itype != 'png'):
                    itype = 'jpeg'
            except:
                print ("Invalid image file name, use default jpeg: %s" % basename)
                itype = 'jpeg'
            self.img_list.append(dict(name=basename, itype=itype))

    def save(self, filename=None):
        if filename == None:
            filename = self.author
        try:
            self.chp_list = sorted(self.chp_list, key=lambda x: x['id'])
            self._init_file(self.oebps_dir, 'coverpage.htm', html_template % {'title': self.title, 'css': self.css_name, 'content': "<img src='%s'></img>" % self.cover})
            self._init_file(self.dest_dir, self.opf_name, self._opf_content())
            self._init_file(self.dest_dir, self.ncx_name, self._ncx_content())
            self._zip_epub('%s.epub' % filename)
            shutil.rmtree(self.dest_dir)
        except:
            print ("build epub fail. temp dir is : %s" % self.dest_dir)
            print (traceback.print_exc())


if __name__ == "__main__":
    import unittest
    
    class TestEpubData(unittest.TestCase):

        def testSave(self):
            pass

    unittest.main()
